/*
 * p2spClient.cc
 *
 *  Created on: 2009-10-31
 *      Author: root
 */

#include "p2sp.h"
#include <random.h>
#define TIME_FORMAT "%.8g"
#define P2SP_JITTER (Random::uniform())

static int header = 0;

static class p2spClientAppClass : public TclClass {
  public:
        p2spClientAppClass() : TclClass("Application/p2sp/p2spClient") {}
        TclObject* create(int, const char*const*) {
          return(new p2spClientApp);
        }
} class_p2sp_client;

void
MsRateTimer::expire(Event*)
{
  t_->update_rate();
  reset(t_->rate_interval_);
}

void
MsRateTimer::set(double t)
{
    sched(t);
}

void
MsRateTimer::reset(double t)
{
    resched(t);
}


void
SrvrReqTimer::expire(Event*)
{
	force_cancel();
	t_->send_srvr_req();
}

void
SrvrReqTimer::set(double t)
{
    sched(t);
}

void
SrvrReqTimer::reset(double t)
{

	resched(t);
	//force_cancel();
	//if(status() == TIMER_HANDLING) assert(false);
	//set(t);
}

void
PeerBcastTimer::expire(Event*)
{
  if(t_->p2sp_enable() == 1) {
      t_->send_peer_data();
  }
  //resched(t_->data_tx_wnd());
}

void
PeerBcastTimer::set(double t)
{
	set_time_ = CURRENT_TIME + t;
	sched(t);
}

void
PeerBcastTimer::reset(double t)
{
	set_time_ = CURRENT_TIME + t;
	resched(t);
}

void
PeerBcastTimer::postpone(double t)
{
    if(status() == TIMER_PENDING && set_time_ - CURRENT_TIME < t)
    	reset(set_time_ - CURRENT_TIME + t);
    else if(status() == TIMER_PENDING) {
    	t_->interrput_ = 1;
    }
}

void
PeerJoinTimer::expire(Event*)
{
  if(t_->p2sp_enable() == 1) {
      t_->send_peer_join();
  }
  //resched(t_->data_tx_wnd());
}

void
PeerJoinTimer::set(double t)
{
    set_time_ = CURRENT_TIME + t;
	sched(t);
}

void
PeerJoinTimer::reset(double t)
{
	set_time_ = CURRENT_TIME + t;
	resched(t);
}

void
PeerJoinTimer::postpone(double t)
{
    if((status() == TIMER_PENDING) && (set_time_ - CURRENT_TIME < t))
    	reset(set_time_ - CURRENT_TIME + t);
}

//Constructor (also init instances of timers
p2spClientApp::p2spClientApp() : running_(0), peer_bcast_timer_(this), srvr_req_timer_(this),
                          state_(IDLE), peer_down_(0), rate_timer_(this), peer_join_timer_(this),
                          srvr_down_(0), peer_up_(0), seqno_(0), interrput_(0), peer_info_up_(0),infomap_size_(16)
{
    bind("chunksize_", &chunksize_);
    bind("filesize_", &filesize_);
    bind("group_size_", &group_size_);
    bind("guard_time_", &guard_time_);
    bind("co_time_", &co_time_);
    bind("p2sp_enable_", &p2sp_enable_);
    bind("rate_interval_", &rate_interval_);
    bind_bw("wwan_rate_", &wwan_rate_);
    bind_bw("wlan_rate_", &wlan_rate_);


/*  bind("peer_req_jitter_",&peer_req_jitter_);
    bind("peer_data_jitter_",&peer_data_jitter_);
    bind("peer_data_timeout_",&peer_data_timeout_);*/
    bind("srvr_data_timeout_",&srvr_data_timeout_);
    bind("infomap_size_", &infomap_size_);
    rate_timer_.set(0.1);
    ms_rate_ = 0;


}

int
p2spClientApp::command(int argc, const char*const* argv) {
  Tcl& tcl = Tcl::instance();

  if(argc == 3) {
      if (strcmp(argv[1], "attach-agent") == 0) {
          agent_ = (Agent*) TclObject::lookup(argv[2]);
          if (agent_ == 0) {
            tcl.resultf("no such agent %s", argv[2]);
            return(TCL_ERROR);
           }
      }
  agent_->attachApp(this);
  return(TCL_OK);
  }

  return (Application::command(argc, argv));

}

//======================================================================
// process received data
// there will be only one packet
// this packet may include data, chunkmap of the source, (random integer of the source)
//
//======================================================================

void
p2spClientApp::process_data(int size, AppData* data) {
		if(data){
			  if(running_) {
					PacketData* p2sp_pkt_data = (PacketData*) data;
					p2sp_data_t* p2sp_data = (p2sp_data_t*)(p2sp_pkt_data->data());

					if(p2sp_data->type_ == IS_RECVING && p2sp_data->src_id_ != srvr_id_) {
							peer_join_timer_.postpone(chk_trans_time_);
							peer_bcast_timer_.postpone(chk_trans_time_);
					}
					else if(p2sp_data->type_ == SRVR_REQUEST) assert(false);
					else if(p2sp_data->type_ == SRVR_DATA) {
							srvr_req_timer_.force_cancel();
							rx_from_srvr(p2sp_data->chunk_id_);
							send_srvr_req();
					}
					else if(p2sp_data->type_ == PEER_INFO) {
						if(is_member_ == 1) {

								//fprintf(stderr, ""TIME_FORMAT"Node %d received peer info from Node %d.\n", CURRENT_TIME, agent_->addr(), p2sp_data->src_id_);
								assert(p2sp_data->group_size_ == group_size_);
								peer_bcast_timer_.force_cancel(); // when a peer packet is received, then cancel current time
								uint16_t round2myturn = (peer_id_ + group_size_ - p2sp_data->peer_id_) % group_size_; // count the number of slot to my turn
								assert(round2myturn > 0);
								//double btime = guard_time_ * round2myturn + (round2myturn > 1? (round2myturn-1)*chk_trans_time_:0); //count time to broadcast if my former all disappear
								double btime = guard_time_ * round2myturn + (round2myturn > 1? chk_trans_time_:0) + co_time_*(peer_id_ >= p2sp_data->peer_id_ ?0:1); //count time to broadcast if my former all disappear
								peer_bcast_timer_.reset(btime); //set timer

								unsigned char *chkmap = new unsigned char[chkmap_size_];
								gen_chkmap(p2sp_data->chkmap_id_, p2sp_pkt_data->data() + sizeof(p2sp_data_t), chkmap);
								//memcpy(chkmap, p2sp_pkt_data->data() + sizeof(p2sp_data_t), chkmap_size_);

								/*for(int i = 0; i < chkmap_size_; i++)
									fprintf(stderr,"0X%02X ", chkmap[i]);
								fprintf(stderr, "\n");*/

								update_chkmap(chkmap, p2sp_data->src_id_);
								delete chkmap;
						} else {
								//===============================
								//get co_time_ to join
							peer_join_timer_.force_cancel();
							if(join_ready_) {
									double join_time = (p2sp_data->group_size_ - p2sp_data->peer_id_) * guard_time_ + (p2sp_data->group_size_ - p2sp_data->peer_id_ -1)*chk_trans_time_; //( (p2sp_data->group_size_ - p2sp_data->peer_id_) > 1? chk_trans_time_:0 );
									peer_join_timer_.reset(join_time);
							} else {
									if(p2sp_data->src_id_ < agent_->addr() && p2sp_data->peer_id_ >= peer_id_) peer_id_ = p2sp_data->peer_id_+1;
									else if(p2sp_data->src_id_ > agent_->addr() && p2sp_data->peer_id_ == peer_id_) {
										join_ready_ = 1;
										group_size_ = p2sp_data->group_size_ + 1;
										double join_time = (p2sp_data->group_size_ - p2sp_data->peer_id_) * guard_time_ + (p2sp_data->group_size_ - p2sp_data->peer_id_ -1)*chk_trans_time_; //( (p2sp_data->group_size_ - p2sp_data->peer_id_) > 1? chk_trans_time_:0 );
										peer_join_timer_.reset(join_time);
									} else if (agent_->addr() > p2sp_data->src_id_ && p2sp_data->peer_id_ + 1 == p2sp_data->group_size_) {
										join_ready_ = 1;
										peer_id_ = p2sp_data->peer_id_ + 1;
										group_size_ = p2sp_data->group_size_ + 1;
										double join_time = guard_time_;
										peer_join_timer_.reset(join_time);
									}
							}

						}


					} else if(p2sp_data->type_ == PEER_DATA) {
						if(is_member_ == 1){

								//fprintf(stderr, "+++++++++++++++++++++++++++++++++++++++++++++++++"TIME_FORMAT"Node %d received peer data %d from Node %d.\n", CURRENT_TIME, agent_->addr(), p2sp_data->chunk_id_, p2sp_data->src_id_);

								assert(p2sp_data->group_size_ = group_size_);
								peer_bcast_timer_.force_cancel(); // when a peer packet is received, then cancel current time
								uint16_t round2myturn = (peer_id_ + group_size_ - p2sp_data->peer_id_) % group_size_; // count the number of slot to my turn
								assert(round2myturn > 0);
								//double btime = guard_time_ * round2myturn + (round2myturn > 1? (round2myturn-1)*chk_trans_time_:0); //count time to broadcast if my former all disappear
								double btime = guard_time_ * round2myturn + (round2myturn > 1? chk_trans_time_:0) + co_time_*(peer_id_ >= p2sp_data->peer_id_ ?0:1); //count time to broadcast if my former all disappear
								peer_bcast_timer_.reset(btime); //set timer

								unsigned char *chkmap = new unsigned char[chkmap_size_];
								gen_chkmap(p2sp_data->chkmap_id_, p2sp_pkt_data->data() + sizeof(p2sp_data_t), chkmap);
								//memcpy(chkmap, p2sp_pkt_data->data() + sizeof(p2sp_data_t), chkmap_size_);

								/*for(int i = 0; i < chkmap_size_; i++)
									fprintf(stderr,"0X%02X ", chkmap[i]);
								fprintf(stderr, "\n");*/

								update_chkmap(chkmap, p2sp_data->src_id_);
								delete chkmap;

								//update_chkmap((unsigned char*)(p2sp_data + sizeof(p2sp_data_t)));
								rx_from_peer(p2sp_data->chunk_id_);
						} else {
							//===============================
							//get co_time_ to join
							peer_join_timer_.force_cancel();
							if(join_ready_) {
								double join_time = (p2sp_data->group_size_ - p2sp_data->peer_id_) * guard_time_ + (p2sp_data->group_size_ - p2sp_data->peer_id_ -1)*chk_trans_time_; //( (p2sp_data->group_size_ - p2sp_data->peer_id_) > 1? chk_trans_time_:0 );
								peer_join_timer_.reset(join_time);
							} else {
								if(p2sp_data->src_id_ < agent_->addr() && p2sp_data->peer_id_ >= peer_id_) peer_id_ = p2sp_data->peer_id_+1;
								else if(p2sp_data->src_id_ > agent_->addr() && p2sp_data->peer_id_ == peer_id_) {
									join_ready_ = 1;
									group_size_ = p2sp_data->group_size_ + 1;
									double join_time = (p2sp_data->group_size_ - p2sp_data->peer_id_) * guard_time_ + (p2sp_data->group_size_ - p2sp_data->peer_id_ -1)*chk_trans_time_; //( (p2sp_data->group_size_ - p2sp_data->peer_id_) > 1? chk_trans_time_:0 );
									peer_join_timer_.reset(join_time);
								} else if (agent_->addr() > p2sp_data->src_id_ && p2sp_data->peer_id_ + 1 == p2sp_data->group_size_) {
									join_ready_ = 1;
									peer_id_ = p2sp_data->peer_id_ + 1;
									group_size_ = p2sp_data->group_size_ + 1;
									double join_time = guard_time_;
									peer_join_timer_.reset(join_time);
								}
							}
						}
					} else if(p2sp_data->type_ == PEER_JOIN) {
						if(is_member_ == 1) {
								//=====================
								//process when receive a peer join request
								//reset co_timer to start peer broadcast
								fprintf(stderr, ""TIME_FORMAT" Node %d received peer join from node %d.\n", CURRENT_TIME, agent_->addr(), p2sp_data->src_id_);
								peer_bcast_timer_.force_cancel();
								if(p2sp_data->src_id_ < agent_->addr()) peer_id_ ++;
								group_size_ ++;
								double btime = co_time_ + peer_id_ * guard_time_ + (peer_id_ > 0 ? chk_trans_time_:0);
								peer_bcast_timer_.reset(btime);
						} else {
								//====================
								//try join next time
								peer_join_timer_.force_cancel();
								join_ready_ = 0;
								peer_id_ = 0;
								double join_time = co_time_ + p2sp_data->group_size_ * guard_time_ + chk_trans_time_; //( (p2sp_data->group_size_ - p2sp_data->peer_id_) > 1? chk_trans_time_:0 );
								peer_join_timer_.reset(join_time);
						}
					}
					//data have been delete in udp-p2sp;
			  }

	  }
}

void
p2spClientApp::send_srvr_req() {
	  //p2spAppData* p2sp_data = new p2spAppData();
	  int index;

	  PacketData* p2sp_pkt_data = new PacketData(sizeof(p2sp_data_t));
	  p2sp_data_t* p2sp_data = (p2sp_data_t*)(p2sp_pkt_data->data());
	  index = select_req_id();
	  assert(index != -1);
	  //fprintf(stderr,"================================================================================================================================"TIME_FORMAT"Node %d send chunk req %d.\n", CURRENT_TIME, agent_->addr(),index);

	  if(running_ &&  index != -1 && rx_done_ != 1 ){
			p2sp_data->type_ = SRVR_REQUEST;

			p2sp_data->nbytes_ = sizeof(p2sp_data_t);
			p2sp_data->chunk_id_ = index; //select a chunk to request
			p2sp_data->src_id_ = agent_->addr();
			p2sp_data->dst_id_ = srvr_id_;
			p2sp_data->seqno_ =  ++seqno_;
			p2sp_data->peer_id_ = peer_id_;
			p2sp_data->group_size_ = group_size_;
			agent_->sendto(p2sp_data->nbytes_, p2sp_pkt_data, 0, srvr_id_);
			srvr_req_timer_.reset(srvr_data_timeout_*(2*sizeof(p2sp_data_t) + chunksize_)*10/wwan_rate_);
	  }

}

void
p2spClientApp::send_peer_data(){
	  if(p2sp_enable_ == 1) {

			if(!running_ )	return;

			peer_bcast_timer_.force_cancel(); // when a peer packet is received, then cancel current time
			uint16_t round2myturn = group_size_ ; // count the number of slot to my turn
			assert(round2myturn > 0);
			double btime = guard_time_ * round2myturn + (round2myturn > 1? round2myturn * chk_trans_time_:0) + co_time_; //count time to broadcast if my former all disappear
//			printf("guard time is %f \t\t; btime is %f\n",guard_time_, btime);
			peer_bcast_timer_.reset(btime); //set timer


			int tx_chunk_id;
			tx_chunk_id = select_tx_chunk();

			if ( tx_chunk_id != -1) {
				PacketData* p2sp_pkt_data = new PacketData(sizeof(p2sp_data_t) + infomap_size_ + chunksize_);
				p2sp_data_t* p2sp_data = (p2sp_data_t*)p2sp_pkt_data->data();
				p2sp_data->nbytes_ = sizeof(p2sp_data_t) + infomap_size_ + chunksize_; // size of p2sp application data packet, size is equal to chunk size
				p2sp_data->type_ = PEER_DATA;
				p2sp_data->chunk_id_ = tx_chunk_id; //change 1 to one choose chunk from request bitmap
				p2sp_data->src_id_ = agent_->addr();
				p2sp_data->dst_id_ = BROADCAST_ID;
				p2sp_data->seqno_ = ++seqno_;
				p2sp_data->peer_id_ = peer_id_;
				p2sp_data->group_size_ = group_size_;
				assert(p2sp_data->peer_id_ < p2sp_data->group_size_);
				//fprintf(stderr,"+++++++++++++++++++++++++++++++++++++++++++++++++"TIME_FORMAT"Node %d send chunk %d, size %d peer_id_ %d, group_size %d. \n", CURRENT_TIME, agent_->addr(),tx_chunk_id, p2sp_data->nbytes_, peer_id_, group_size_);

				/*for(int i = 0; i < chkmap_size_; i++)
					fprintf(stderr,"0X%02X ", b_chkmap_[i]);
				fprintf(stderr, "\n");*/

				unsigned char infomap[infomap_size_];
				p2sp_data->chkmap_id_ = get_infomap(infomap);

				memcpy(p2sp_pkt_data->data() + sizeof(p2sp_data_t), infomap, infomap_size_);
				agent_->sendto(p2sp_pkt_data->size(), p2sp_pkt_data, 0, BROADCAST_ID);
				peer_up_ ++;
				reset_pri(tx_chunk_id);

			} else {
				int send_info = 0;
				for(int i = 0; i < group_size_; i ++) if(i != agent_->addr() && all_same_[i] == 0 ) {send_info = 1; same_conf_ = 2; break;}
				if(send_info == 0 && same_conf_ == 0) return;
				else if(send_info == 0) same_conf_ --;
				PacketData* p2sp_pkt_data = new PacketData(sizeof(p2sp_data_t) + infomap_size_);
				p2sp_data_t* p2sp_data = (p2sp_data_t*)p2sp_pkt_data->data();
				p2sp_data->nbytes_ = sizeof(p2sp_data_t)  + infomap_size_; // size of p2sp application data packet, size is equal to chunk size
				p2sp_data->type_ = PEER_INFO;
				p2sp_data->chunk_id_ = 0;
				p2sp_data->src_id_ = agent_->addr();
				p2sp_data->dst_id_ = BROADCAST_ID;
				p2sp_data->seqno_ = ++seqno_;
				p2sp_data->peer_id_ = peer_id_;
				p2sp_data->group_size_ = group_size_;
				//fprintf(stderr, ""TIME_FORMAT" Node %d send peer info. %d, %d\n", CURRENT_TIME, agent_->addr(),p2sp_data->nbytes_, p2sp_pkt_data->size());

				/*for(int i = 0; i < chkmap_size_; i++)
					fprintf(stderr,"0X%02X ", b_chkmap_[i]);
				fprintf(stderr, "\n");*/
				unsigned char infomap[infomap_size_];
				p2sp_data->chkmap_id_ = get_infomap(infomap);

				memcpy(p2sp_pkt_data->data() + sizeof(p2sp_data_t), infomap, infomap_size_);
				agent_->sendto(p2sp_pkt_data->size(), p2sp_pkt_data, 0, BROADCAST_ID);

				peer_info_up_++;

			}


	  }
}

void
p2spClientApp::send_peer_join(){
	if(p2sp_enable_ == 1) {
			if(!running_ )	return;
			if(is_member_ == 1) {
					peer_join_timer_.force_cancel();
					return;
			}
			peer_join_timer_.force_cancel();
			PacketData* p2sp_pkt_data = new PacketData(sizeof(p2sp_data_t));
			p2sp_data_t* p2sp_data = (p2sp_data_t*)p2sp_pkt_data->data();
			p2sp_data->nbytes_ = sizeof(p2sp_data_t); // size of p2sp application data packet, size is equal to chunk size
			p2sp_data->type_ = PEER_JOIN;
			p2sp_data->chunk_id_ = 0;
			p2sp_data->src_id_ = agent_->addr();
			p2sp_data->dst_id_ = BROADCAST_ID;
			p2sp_data->seqno_ = ++seqno_;
			p2sp_data->peer_id_ = peer_id_;
			p2sp_data->group_size_ = group_size_;
			assert(peer_id_ < group_size_);
			is_member_ = 1;
			fprintf(stderr, ""TIME_FORMAT" Node %d broadcast peer join.\n", CURRENT_TIME, agent_->addr());
			agent_->sendto(p2sp_pkt_data->size(), p2sp_pkt_data, 0, BROADCAST_ID);

			//peer_join_timer_.reset(join_time);
	}
}

void
p2spClientApp::rx_from_srvr(uint16_t chunkid) {

		//fprintf(stderr, "================================================================================================================================"TIME_FORMAT"Node %d received chunk %d from server. \n", CURRENT_TIME, agent_->addr(), chunkid);

		set_chkmap(w_chkmap_, chunkid);	//update the set of chunks received from WWAN
		int r = set_chkmap(b_chkmap_, chunkid); //update the set of chunks received from both Interface
		set_chkmap(c_chkmap_, chunkid); //update the set of chunks received by the community

		if(r == 0)	{
			add_bytes_ += chunksize_;
			srvr_down_ ++;
			for(int i = 0; i < group_size_; i++) {
				if(i != agent_->addr()) {all_same_[i] = 0; same_conf_ = 2; }
				else same_conf_ = 2;
			}
		}

		for(int i = 0; i < chkmap_size_; i++)
			if(b_chkmap_[i] != 0XFF) return;
		rx_done_ = 1;
		fprintf(stderr,""TIME_FORMAT"\t Node %d receive down.\n", CURRENT_TIME, agent_->addr());
}

void
p2spClientApp::rx_from_peer(uint16_t chunkid) {
		//fprintf(stderr, ""TIME_FORMAT"\t Node %d received chunk %d from peer. \n", CURRENT_TIME, agent_->addr(), chunkid);

		set_chkmap(l_chkmap_, chunkid); //update the set of chunks received from WLAN
		int r = set_chkmap(b_chkmap_, chunkid); //update the set of chunks received from both Interface
		set_chkmap(c_chkmap_, chunkid); //update the set of chunks received by the community

		if(r == 0)	{
			peer_down_ ++;
			add_bytes_ += chunksize_;
		}

		reset_pri(chunkid);
		for(int i = 0; i < chkmap_size_; i++)
			if(b_chkmap_[i] != 0XFF) return;
		rx_done_ = 1;

}

void
p2spClientApp::init() {
	  int nChunks = filesize_/chunksize_ + (filesize_%chunksize_? 1:0);
	  chkmap_size_ = nChunks / 8 + (nChunks % 8 ? 1:0);
	  chk_trans_time_ = 5*(chunksize_ + infomap_size_ + sizeof(p2sp_data_t))* 10 / wlan_rate_;
	  guard_time_ = guard_time_ * (infomap_size_ + sizeof(p2sp_data_t)) * 10 / wlan_rate_;
	  co_time_ =  co_time_ * group_size_ * guard_time_;
	  peer_id_ = 0;
	  com_size_ = group_size_;
	  group_size_ = 1;
	  rx_done_ = 0;
	  next_chunk_ = 0;
	  assert(chkmap_size_ < MAX_CHKMAP);
	  for(int i = 0; i < chkmap_size_; i ++) {
			  //init w_chkmap_
			  w_chkmap_[i] = 0;
			  //init l_chkmap_
			  l_chkmap_[i] = 0;
			  //init w_chkmap_
			  b_chkmap_[i] = 0;
			  //init w_chkmap_
			  c_chkmap_[i] = 0;
	  }
	  if(nChunks % 8) {
		  unsigned char off = 1;
		  //set the lowest (8-(nChunks % 8)) bits to 1;
		  for(int i = 0; i < 7-(nChunks % 8); i ++) {
			  off =  off << 1 | 1;
		  }
		  w_chkmap_[chkmap_size_ - 1] |= off;
		  l_chkmap_[chkmap_size_ - 1] |= off;
		  b_chkmap_[chkmap_size_ - 1] |= off;
		  c_chkmap_[chkmap_size_ - 1] |= off;
	  }
	  //set priority map for all chunks to zero
	  for(int j = 0; j < nChunks; j++) {
			  primap_[j] = 0;
	  }

	  for(int k = 0; k < group_size_; k++){
			  all_same_[k] = 1;
	  }
	  same_conf_ = 2;

}


void
p2spClientApp::start() {
		fprintf(stderr, ""TIME_FORMAT"\t Node %d Start p2sp App Client\n", CURRENT_TIME, agent_->addr());
		running_ = 1;
		is_member_ = 0;
		join_ready_ = 0;

		start_time_ = CURRENT_TIME;
		srvr_id_ = agent_->daddr();
		init();
		send_srvr_req();
		if (p2sp_enable() && agent_->addr() == 0){
			  is_member_ = 1;
			  join_ready_ = 1;
			  peer_bcast_timer_.reset(0.2); //snd peer data after a jitter time
			  //peer_bcast_timer_.reset(P2SP_JITTER * 5 * (chkmap_size_ + chunksize_) *10/wwan_rate_); //snd peer data after a jitter time
		}

}


void
p2spClientApp::stop() {
  fprintf(stderr, ""TIME_FORMAT"\t Node %d Stop p2sp App Client\n", CURRENT_TIME, agent_->addr());
  running_ = 0;
}

void
p2spClientApp::reset_pri(uint16_t chunkid){
		// reset chunkid's sending priority
		primap_[chunkid] = 0;
}

int
p2spClientApp::set_chkmap(unsigned char *chkmap, uint16_t chunkid) {
	//set zero based chunkid to chkmap
	int ret = chkmap[chunkid/8] >> (7 - (chunkid % 8)) & 1;
	chkmap[chunkid/8] = chkmap[chunkid/8] | (0x80 >> (chunkid % 8));
	return ret;
}

void
p2spClientApp::update_chkmap(unsigned char *chkmap, nsaddr_t src_id) {
	// update chunk map based on received chkmap
	// priority map is also update at the same time
//	unsigned char *tmp_chkmap = new unsigned char[chkmap_size_];
	int is_same = 1;
	for(int i = 0; i < chkmap_size_; i++){
		c_chkmap_[i] = c_chkmap_[i] | chkmap[i];
//		tmp_chkmap[i] = ((chkmap[i]^b_chkmap_[i]) & b_chkmap_[i]); // find the indexes of chunks that local has but the other hasn't
		for(int j = 0; j < 8; j++)
			primap_[i * 8 + j] += ((((chkmap[i]^b_chkmap_[i]) & b_chkmap_[i]) >> (7-j)) & 1); // incr priority for those I have but others want
		if(b_chkmap_[i] == chkmap[i] && is_same == 1) {all_same_[src_id] = 1;}
		else {all_same_[src_id] = 0; same_conf_ = 2; is_same = 0;}
	}
//	delete tmp_chkmap;
}

//======================================================================
// return whether local peer has the chunk
// input: chunkid
// output: boolean value (true: has it; false: hasn't)
//======================================================================

bool
p2spClientApp::bhasChunk(uint16_t chunkid) {

	if(((b_chkmap_[chunkid / 8] >> (7- chunkid % 8)) & 1) == 0) {
//		printf("Node %d has chunk %d in BSET. \n", agent_->addr(), chunkid);
		return true;
	}


}

bool
p2spClientApp::whasChunk(uint16_t chunkid) {

	if(((w_chkmap_[chunkid / 8] >> (7- chunkid % 8)) & 1) == 0){
//		printf("Node %d has chunk %d in WSET. \n", agent_->addr(), chunkid);
		return true;
	}


}

int
p2spClientApp::select_tx_chunk() {
  //uint16_t tx_Pending_[MAXBMPS*32];
	  int index = -1;
	  int pending = 0;
	  for(int i =0; i<chkmap_size_ * 8; i++) {
		  if (whasChunk(i) && pending < primap_[i]) {
				  pending = primap_[i];
				  index = i;
		  }
	  }
	  if(index != -1) {
//		  printf("Node %d select chunk %d to sent. \n", agent_->addr(), index);
		  return index;
	  }
	//  printf("Node %d select chunk %d\n", agent_->addr(), index);
	  for(int i =0; i<chkmap_size_ * 8; i++) {
			if (bhasChunk(i) && pending < primap_[i]) {
					pending = primap_[i];
					index = i;
			}
	  }
//	  printf("Node %d select chunk %d to sent. \n", agent_->addr(), index);
	  return index;
}

int
p2spClientApp::select_req_id(){

	if(p2sp_enable() == 0 || is_member_ == 0)
		return next_chunk_++;

	int index  = -1;
	int i,j;

	for(i = 0; i < chkmap_size_; i++){
			if (c_chkmap_[i] == 0XFF) continue;
			for(j = 0; j < 8; j++){
				if((c_chkmap_[i] >> (7 - j) & 1) == 0 &&  (i*8+j) % group_size_ - peer_id_ == 0) return i*8+j;

			}
	}
	for(i = 0; i < chkmap_size_; i++){
			if (b_chkmap_[i] == 0XFF) continue;
			for(j = 0; j < 8; j++){
				if((b_chkmap_[i] >> (7 - j) & 1) == 0  && (i*8+j) % group_size_ - peer_id_ == 0) return i*8+j;
			}
	}

	return index;
}

int
p2spClientApp::get_infomap(unsigned char *outchkmap) {
	int index = 0;
	for (index = 0; index < chkmap_size_; index++) {
		if(b_chkmap_[index] != 0XFF) {
			if(index + infomap_size_ < chkmap_size_) memcpy(outchkmap, b_chkmap_+index, infomap_size_);
			else memcpy(outchkmap, b_chkmap_+index, chkmap_size_ - index);
			return index;
		}
	}

	return index - 1;
}

void
p2spClientApp::gen_chkmap(int index, unsigned char *inchkmap, unsigned char *outchkmap) {
	int i;
	assert(index < chkmap_size_);
	for(i = 0; i < index; i++)	outchkmap[i] = 0XFF;
	for(i = index; i < chkmap_size_; i ++) outchkmap[i] = 0;
	if(index + infomap_size_ < chkmap_size_) memcpy(outchkmap + index, inchkmap, infomap_size_);
	else memcpy(outchkmap+index, inchkmap, chkmap_size_ - index);
}


void
p2spClientApp::recv_msg(int nbytes, const char* msg) {

}



void
p2spClientApp::update_rate() {
	ms_rate_ = ms_rate_  * 0.6 + add_bytes_ / rate_interval_ *0.4;
	add_bytes_ = 0;

//	if( header == 0 ){
//			header = 1;
//			char logStart[256];
//			sprintf(logStart,"GroupSize,NodeId,Time,Rate,ServerDown,PeerDown,PeerUp,ChunkSize,WLANRate\n");
//
//			/*if(p2sp_enable() == 0) {
//				if(remove("ind.csv") != 0)
//					fprintf(stderr, "No file is deleted!");
//				else
//					fprintf(stderr, "ind.csv is deleted!");
//			}else{
//				if(remove("cor.csv") != 0)
//					fprintf(stderr, "No file is deleted!");
//				else
//					fprintf(stderr, "cor.csv is deleted!");
//			}
//*/
//
//			FILE *tfp;
//			if(p2sp_enable() == 0)	tfp = fopen("ind.csv", "a");
//			else tfp = fopen("cor.csv", "a");
//
//			if (tfp != NULL){
//				  fseek(tfp, 0, SEEK_END);
//				  fputs(logStart, tfp);
//				  fclose(tfp);
//			} else
//				assert(false);
//	}

	char logRate[128];
	sprintf(logRate,"%d,%d,"TIME_FORMAT",%f,%d,%d,%d,%d,%f\n", com_size_, agent_->addr(), CURRENT_TIME,  ms_rate_, srvr_down_, peer_down_, peer_up_, chunksize_, wlan_rate_);
	FILE *tfp;
	if(p2sp_enable() == 0) tfp = fopen("ind.csv", "a");
	else tfp = fopen("cor.csv", "a");

	if (tfp != NULL){
			fseek(tfp, 0, SEEK_END);
			fputs(logRate, tfp);
			fclose(tfp);
	}else assert(false);

	//	delete logRate;


}


//==============================================================
//functions that may not be used in new method
//==============================================================

// send_peer_req
// will be replaced by the chunkmap advertise in each data packet
/*
void
p2spClientApp::send_peer_req() {

  if(p2sp_enable_ == 1) {
      //p2spAppData* p2sp_data = new p2spAppData();
      PacketData* p2sp_pkt_data = new PacketData(sizeof(p2sp_data_t)+nChunks_);
      p2sp_data_t* p2sp_data = (p2sp_data_t*)(p2sp_pkt_data->data());

      if(running_){
            p2sp_data->type_ = PEER_REQUEST;
            memcpy(p2sp_pkt_data->data() + sizeof(p2sp_data_t), req_bitmap, nChunks_);
            p2sp_data->nbytes_ = sizeof(p2sp_data_t)+nChunks_;
            p2sp_data->src_id_ = agent_->addr();
            p2sp_data->dst_id_ = BROADCAST_ID;
            p2sp_data->seqno_ = ++seqno_;

//            printf(""TIME_FORMAT"\t P2SP: Client %d send peer request\n", CURRENT_TIME, agent_->addr());
            agent_->sendto(p2sp_data->nbytes_, p2sp_pkt_data, 0, BROADCAST_ID);
         }
  }
}*/

// updatePendingBmp
// will be replaced by the priority process
// include update_primap(*chunkmap), incr_pri(chunkid) and reset_pri(chunkid)
/*void
p2spClientApp::updatePendingBmp(unsigned char *rq_bmp) {
  nsaddr_t id;
  id = agent_->addr();
  for(int i=0; i<nChunks_; i++)
      for(int j=0; j < 8; j++)
            tx_Pending_[i*8 + j] += (rq_bmp[i] >> j) & 1;
}*/

/*void
p2spClientApp::resetPendingBit(uint16_t chunkid) {
  assert(chunkid < 8*nChunks_);
  tx_Pending_[chunkid] = 0;
}*/




//====================================================================
// we don't care the fetch finish time
// so it doesn't matter whether fetching is finished
// rx_done is deprecated.
//====================================================================


/*
void
p2spClientApp::rx_done() {
  //record time to finish stripping
  char logEnd[256];

  fetch_time_ = CURRENT_TIME - start_time_;

  sprintf(logEnd,"WWANrate:\t%f\tWLANrate:\t%f\tgroupsize:\t%d\tchunksize:\t%d\tNode\t%d\tstarttime:\t"TIME_FORMAT"\tfetchtime:\t"TIME_FORMAT"\twsize:\t%d\tlsize:\t%d\tUpsize:\t%d\treqjitter:\t%.5f\tdatajitter:\t%.5f\n",
      wwan_rate_, wlan_rate_, group_size_, chunksize_, agent_->addr(), start_time_, fetch_time_, srvr_down_, peer_down_,
      peer_up_, peer_data_jitter_, peer_req_jitter_);

  FILE *tfp;
  tfp = fopen("results.txt", "a");

  if (tfp != NULL){
    fseek(tfp, 0, SEEK_END);
    fputs(logEnd, tfp);
    fclose(tfp);
  }


  printf("%s", logEnd);

  req_timer_.force_cancel();
  srvr_req_timer_.force_cancel();

}*/

//====================================================================
// We don't use the contention mechanism any more
// data_tx_wnd is deprecated.
//====================================================================

/*double
p2spClientApp::data_tx_wnd() {
  double tx_wnd;
  if(peer_down_ == 0 && peer_up_ == 0) {
      tx_wnd = max_tx_wnd_;
  }else if(peer_down_ == 0) {
      tx_wnd = group_size_*max_tx_wnd_;
  }else {
      tx_wnd = max_tx_wnd_*peer_up_*group_size_/peer_down_;
  }
  return Random::uniform(tx_wnd);
}*/

//====================================================================
// Timer operations are simplified
// There may be only one unique timer used in the new methed
//====================================================================

/*

void
ReqTimer::expire(Event*)
{
  if(t_->p2sp_enable() == 1) {
        force_cancel();
        deadline = 0;
        t_->send_peer_req();
        reset(t_->peer_data_timeout_ * t_->chunksize_ * 10 / t_->wlan_rate_ + t_->peer_data_jitter_);

  }
}

void
ReqTimer::set(double t)
{
  sched(t);
  deadline = CURRENT_TIME + t;

}

void
ReqTimer::reset(double t)
{
  resched(t);
  deadline = CURRENT_TIME + t;

}

void
ReqTimer::freeze()
{
        force_cancel();
        if(deadline != 0){
        remain_time = deadline - CURRENT_TIME;
        assert(remain_time >= 0);}
}

void
ReqTimer::resume()
{
  if(remain_time != -1){
    reset(remain_time);
    remain_time = -1;
  }

}



void
DataTxTimer::expire(Event*)
{
  if(t_->p2sp_enable() == 1) {
      force_cancel();
      deadline = 0;
      t_->send_peer_data();
  }
  //resched(t_->data_tx_wnd());
}

void
DataTxTimer::set(double t)
{
    sched(t);
    deadline = CURRENT_TIME + t;
}

void
DataTxTimer::reset(double t)
{
    resched(t);
    deadline = CURRENT_TIME + t;
}

void
DataTxTimer::freeze()
{
        force_cancel();
        if(deadline != 0){
        remain_time = deadline - CURRENT_TIME;
        assert(remain_time >= 0);}
}

void
DataTxTimer::resume()
{
  if(remain_time != -1){
    reset(remain_time);
    remain_time = -1;
  }

}*/

//======================================================================
// Assemble data
// Update equivalent rate measurement
// Trace the rate value
// input: chunkid
// output: void
//======================================================================

/*void
p2spClientApp::assemble(uint16_t chunkid) {
	  //assemble data
	  //set req bitmap chunkid = 0 //no more required
	  req_bitmap[chunkid / 8] &= ~(1 << (chunkid % 8));
	  for(int i = 0; i < nChunks_; i++)
		  if(req_bitmap[i])
			   return;
	  //all data received
	  rx_done_ = true;
	  rx_done();

}*/


//======================================================================
// drop data
//======================================================================

/*void
p2spClientApp::dropchunk() {
  //add process later

}*/

